using System;
using System.Collections;
using System.Drawing;
using System.IO;
using System.Globalization;
using System.Threading;
#if !PocketPC || DesignTime
using System.ComponentModel;
#endif

using GeoFramework;
using GeoFramework.IO.Serial;
using GeoFramework.IO;

namespace GeoFramework.Gps.Emulators
{
	/// <summary>
	/// Represents an emulator which obtains its contents line-by-line from a file.
	/// </summary>
    /// <remarks>This class is typically used to replay a previously-recorded
    /// log file while maintaining control over the playback speed.  While a
    /// FileStream can be passed directly to an Interpreter, the interpreter
    /// gives no control over playback speed and will therefore process the file 
    /// as rapidly as possible.  The ReadInterval property controls the delay 
    /// between each sentence.  When the end of the file is reached, the file
    /// is closed, then reopened, and processing starts from the beginning.</remarks>
	public sealed class TextFileEmulator : Emulator
	{
        private string _FileName;
        private StreamReader pReader;
        private TimeSpan pReadInterval = TimeSpan.FromSeconds(1);

        public event EventHandler EndOfFileReached;

        /// <summary>
        /// Creates a new instance using the specified file name.
        /// </summary>
        /// <param name="filename">A path to a file containing text data.</param>
        /// <remarks>This constructor will open a new FileStream using the specified file
        /// and begin sending data.  The file must contain text data with each
        /// sentence separated by a carriage return and line feed.</remarks>
		public TextFileEmulator(string filename)
		{
            if (!File.Exists(filename))
                throw new FileNotFoundException("The specified text file \"" + filename + "\" could not be found.");

            // Create a new reader
            _FileName = filename;
            pReader = new StreamReader(_FileName);   
			this.BaudRate = GeoFramework.IO.Serial.BaudRate.Baud57600;
		}

        /// <summary>
        /// Creates a new instance using the specified file.
        /// </summary>
        /// <param name="file">A FileStream of a file containing text data.</param>
        /// <remarks>This constructor will use the specified FileStream and begin sending data.  
        /// The file must be opened for read access and must contain text data.  Each
        /// sentence in the file must be separated by a carriage return and line feed.</remarks>
        public TextFileEmulator(FileStream stream)
        {
            _FileName = stream.Name;
            pReader = new StreamReader(stream);
			this.BaudRate = GeoFramework.IO.Serial.BaudRate.Baud57600;
        }

        /// <summary>
        /// Creates a new instance using the specified file name.
        /// </summary>
        /// <param name="filename">A path to a file containing text data.</param>
        /// <remarks>This constructor will open a new FileStream using the specified file
        /// and begin sending data.  The file must contain text data with each
        /// sentence separated by a carriage return and line feed.</remarks>
        public TextFileEmulator(string filename, TimeSpan readInterval)
            : this(filename)
        {
            pReadInterval = readInterval;
        }

        /// <summary>
        /// Creates a new instance using the specified file.
        /// </summary>
        /// <param name="file">A FileStream of a file containing text data.</param>
        /// <remarks>This constructor will use the specified FileStream and begin sending data.  
        /// The file must be opened for read access and must contain text data.  Each
        /// sentence in the file must be separated by a carriage return and line feed.</remarks>
        public TextFileEmulator(FileStream stream, TimeSpan readInterval)
            : this(stream)
        {
            pReadInterval = readInterval;
        }

        public override Emulator Clone()
        {
            return new TextFileEmulator(_FileName, pReadInterval);
        }

        public override void Close()
        {
            try
            {
                if (pReader != null)
                    pReader.Close();
            }
            catch
            {
                throw;
            }
            finally
            {
                base.Close();
            }
        }

        private void OnEndOfFileReached()
        {
            // And raise an event
            if (EndOfFileReached != null)
#if PocketPC && !Framework20
			    EndOfFileReached(this, EventArgs.Empty);
#else
                DynamicEvent.Invoke(EndOfFileReached, this, EventArgs.Empty);
#endif
        }

        protected override void OnEmulation()
        {
            // Re-open the stream if necessary
            if (pReader.Peek() == -1)
            {
                pReader.Close();

                // Signal the end of the file
                OnEndOfFileReached();

                // And re-open the same stream
                pReader = new StreamReader(_FileName);
            }

            // Read the next line
            WriteLineToClient(pReader.ReadLine());

            // And sleep the desired interval
#if PocketPC
            Thread.Sleep((int)pReadInterval.TotalMilliseconds);
#else
            Thread.Sleep(pReadInterval);
#endif
        }

        public override string Protocol
        {
            get
            {
                return "NMEA-0183";
            }
        }

        public override string ToString()
        {
            return "Text File Emulator [" + _FileName + "]";
        }

        /// <summary>
        /// Controls the length of time between reading each line of the file.
        /// </summary>
        /// <remarks>This property controls the delay which occurs after sending a single
        /// line of data from the file to the client.  This value is typically one second
        /// but can be changed to any value.  A value of zero seconds will cause the
        /// file to be processed as rapidly as possible.  NOTE: The interval is rounded
        /// to the nearest millisecond.</remarks>
        public TimeSpan ReadInterval
        {
            get
            {
                return pReadInterval;
            }
            set
            {
                pReadInterval = value;
            }
        }

        /// <summary>
        /// Returns the filename of the file being fed into the emulator.
        /// </summary>
        /// <remarks>This property is set via the constructor.  The filename
        /// points to the file which is being read line-by-line and sent to the
        /// client as emulated data.</remarks>
        public string FileName
        {
            get
            {
                return _FileName;
            }
        }
	}
}
